package lib

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/ochinchina/supervisord/tool"
	"github.com/pkg/errors"
	"github.com/spf13/viper"
)

var configPath = "D:/__SYNC2/go-path/src/zollty.com/buddha-agent/config2.yml"

var defaultConfig = Config{
	Buddhad: &BuddhadConfig{
		Logfile:         "%(here)s/buddhad.log",
		LogfileMaxbytes: "50MB",
		LogfileBackups:  10,
		Loglevel:        "info",
		Pidfile:         "%(here)s/buddhad.pid",
		Identifier:      "buddhad",
	},
	BuddhadCtl: &BuddhadCtlConfig{
		Serverurl: "unix:///tmp/buddhad.sock",
		Username:  "read",
		Password:  "1qaz2WSX",
	},
	InetHttpServer: &InetHttpServerConfig{
		Port:     ":9001",
		Username: "read",
		Password: "1qaz2WSX",
	},
	UnixHttpServer: &UnixHttpServerConfig{
		File:     "/tmp/buddhad.sock",
		Username: "read",
		Password: "{SHA}c50268f3176229d8b64ccbe43dd89acc07da355f",
	},
}

var defaultProgram = ProgramConfig{
	Numprocs:                1,
	Autostart:               true,
	Order:                   500,
	Startsecs:               10,
	Startretries:            3,
	Autorestart:             "unexpected",
	Restartpause:            10,
	Exitcodes:               []int{0, 2},
	Stopsignal:              []string{"TERM"},
	Stopwaitsecs:            20,
	Stopchildren:            true,
	Killchildren:            true,
	Redirect_stderr:         true,
	Stdout_logfile:          "/dev/null",
	Stdout_logfile_maxbytes: "50MB",
	Stdout_logfile_backups:  10,
	Stdout_capture_maxbytes: 0,
	Stdout_events_enabled:   true,
	Serverurl:               "AUTO",
}

func GetDefaultProgram() ProgramConfig {
	return defaultProgram
}

// Configuration are the available config values
type Config struct {
	Buddhad        *BuddhadConfig        `mapstructure:"buddhad"`
	BuddhadCtl     *BuddhadCtlConfig     `mapstructure:"buddhad_ctl"`
	InetHttpServer *InetHttpServerConfig `mapstructure:"inet_http_server"`
	UnixHttpServer *UnixHttpServerConfig `mapstructure:"unix_http_server"`
	Programs       []ProgramConfig       `mapstructure:"programs"`
	Groups         []GroupConfig         `mapstructure:"groups"`
	Includes       []string              `mapstructure:"includes"`
	Monitor        MonitorConfig         `mapstructure:"monitor"`
	InfluxConfig   InfluxConfig          `mapstructure:"influx"`
}

type BuddhadConfig struct {
	Logfile         string `mapstructure:"logfile"`
	LogfileMaxbytes string `mapstructure:"logfileMaxbytes"`
	LogfileBackups  int    `mapstructure:"logfileBackups"`
	Loglevel        string `mapstructure:"loglevel"`
	Pidfile         string `mapstructure:"pidfile"`
	Identifier      string `mapstructure:"identifier"`
}

// LogLevel     : debug,info,warn,error,dpanic,panic,fatal
//                default info
// Format       : json, console
//                default json
// Directory    : Log storage path
//                default "./"
type BuddhadCtlConfig struct {
	Serverurl string `mapstructure:"serverurl"`
	Username  string `mapstructure:"username"`
	Password  string `mapstructure:"password"`
}

type InetHttpServerConfig struct {
	Port     string `mapstructure:"port"`
	Username string `mapstructure:"username"`
	Password string `mapstructure:"password"`
}

type UnixHttpServerConfig struct {
	File     string `mapstructure:"file"`
	Username string `mapstructure:"username"`
	Password string `mapstructure:"password"`
}

type ProgramConfig struct {
	Name                    string             `mapstructure:"name"`
	Command                 string             `mapstructure:"command"`
	StartCmd                string             `mapstructure:"start_cmd"`
	StopCmd                 string             `mapstructure:"stop_cmd"`
	ProcessName             string             `mapstructure:"process_name"`
	AppPidPath              string             `mapstructure:"app_pid_path"`
	Priority                int                `mapstructure:"priority"`
	Order                   int                `mapstructure:"order"`
	Numprocs                int                `mapstructure:"numprocs"`
	Autostart               bool               `mapstructure:"autostart"`
	Startsecs               int                `mapstructure:"startsecs"`
	Startretries            int                `mapstructure:"startretries"`
	Restartpause            int                `mapstructure:"restartpause"`
	Autorestart             string             `mapstructure:"autorestart"`
	Exitcodes               []int              `mapstructure:"exitcodes"`
	Stopsignal              []string           `mapstructure:"stopsignal"`
	Stopwaitsecs            int                `mapstructure:"stopwaitsecs"`
	Killwaitsecs            int                `mapstructure:"killwaitsecs"`
	Stopasgroup             bool               `mapstructure:"stopasgroup"`
	Stopchildren            bool               `mapstructure:"stopchildren"`
	Killasgroup             bool               `mapstructure:"killasgroup"`
	Killchildren            bool               `mapstructure:"killchildren"`
	User                    string             `mapstructure:"user"`
	Redirect_stderr         bool               `mapstructure:"redirect_stderr"`
	Stdout_logfile          string             `mapstructure:"stdout_logfile"`
	Stdout_logfile_maxbytes string             `mapstructure:"stdout_logfile_maxbytes"`
	Stdout_logfile_backups  int                `mapstructure:"stdout_logfile_backups"`
	Stdout_capture_maxbytes int                `mapstructure:"stdout_capture_maxbytes"`
	Stdout_events_enabled   bool               `mapstructure:"stdout_events_enabled"`
	Environment             []string           `mapstructure:"environment"`
	Directory               string             `mapstructure:"directory"`
	Serverurl               string             `mapstructure:"serverurl"`
	DependsOn               string             `mapstructure:"depends_on"`
	SaveLogC0               []string           `mapstructure:"save_log_c0"`
	SaveLogC1               []string           `mapstructure:"save_log_c1"`
	SaveLogC2               []string           `mapstructure:"save_log_c2"`
	SaveLogC3               []string           `mapstructure:"save_log_c3"`
	Event                   []EventConfig      `mapstructure:"event"`
	EventProcess            EventProcessConfig `mapstructure:"event_process"`
	// 程序中自动赋值
	NumprocsStart int `mapstructure:"numprocs_start"`
	ProcessNum    int `mapstructure:"process_num"`
}

type GroupConfig struct {
	Name     string `mapstructure:"name"`
	Programs string `mapstructure:"programs"`
	Priority int    `mapstructure:"priority"`
	Order    int    `mapstructure:"order"`
}

// type SaveLogConfig struct {
// 	C0 []string `mapstructure:"c0"`
// 	C1 []string `mapstructure:"c1"`
// 	C2 []string `mapstructure:"c2"`
// 	C3 []string `mapstructure:"c3"`
// }

type EventConfig struct {
	Type    string   `mapstructure:"type"`
	Actions []string `mapstructure:"actions"`
}

type EventProcessConfig struct {
	CpuLimit int `mapstructure:"cpu_limit"`
	MemLimit int `mapstructure:"mem_limit"`
}

type EventOsConfig struct {
	CpuLimit   int      `mapstructure:"cpu_limit"`
	MemLimit   int      `mapstructure:"mem_limit"`
	Load1Limit int      `mapstructure:"load1_limit"`
	Actions    []string `mapstructure:"actions"`
}

type MonitorConfig struct {
	DataPath        string         `mapstructure:"data_path"`
	EventOs         EventOsConfig  `mapstructure:"event_os"`
	AlarmDingtalkC0 DingtalkConfig `mapstructure:"alarm_dingtalk_c0"`
	AlarmDingtalkC1 DingtalkConfig `mapstructure:"alarm_dingtalk_c1"`
}

type DingtalkConfig struct {
	Secret      string `mapstructure:"secret"`
	AccessToken string `mapstructure:"access_token"`
}

type InfluxConfig struct {
	ServerURL string `mapstructure:"server_url"`
	AuthToken string `mapstructure:"auth_token"`
	Org       string `mapstructure:"org"`
	Bucket    string `mapstructure:"bucket"`
}

// IsFile checks whether the path is a file,
// it returns false when it's a directory or does not exist.
func IsFile(fp string) bool {
	f, e := os.Stat(fp)
	if e != nil {
		return false
	}
	return !f.IsDir()
}

// func (a *DatabaseConfig) DSN() string {
// 	return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?%s", a.Username, a.Password, a.Host, a.Port, a.Name, a.Parameters)
// }

// func (a *HttpConfig) ListenAddr() string {
// 	if err := validator.New().Struct(a); err != nil {
// 		return "0.0.0.0:5100"
// 	}

// 	return fmt.Sprintf("%s:%d", a.Host, a.Port)
// }

// func (a *RedisConfig) Addr() string {
// 	return fmt.Sprintf("%s:%d", a.Host, a.Port)
// }

func SetConfigPath(path string) {
	if !IsFile(path) {
		panic("config filepath does not exist")
	}

	configPath = path
}

func NewConfig(configPath string) *Config {
	config := defaultConfig

	viper.SetConfigFile(configPath)
	fmt.Println("\nload config from", configPath)
	if err := viper.ReadInConfig(); err != nil {
		panic(errors.Wrap(err, "failed to read config"))
	}

	if err := viper.Unmarshal(&config); err != nil {
		panic(errors.Wrap(err, "failed to marshal config"))
	}
	fmt.Printf("\nconfig value: %v", config)

	if config.Includes != nil {
		if config.Programs == nil {
			config.Programs = []ProgramConfig{}
		}
		if config.Groups == nil {
			config.Groups = []GroupConfig{}
		}
		configFileDir := filepath.Dir(configPath)
		env := tool.NewStringExpression("here", configFileDir)
		includeFiles := tool.GetIncludeFiles(configFileDir, env, config.Includes)
		for _, cf := range includeFiles {
			fmt.Println("\nload config from", cf)
			cProgtmp := defaultConfig
			viper.SetConfigFile(cf)
			if err := viper.ReadInConfig(); err != nil {
				panic(errors.Wrap(err, "failed to read config"))
			}
			if err := viper.Unmarshal(&cProgtmp); err != nil {
				panic(errors.Wrap(err, "failed to marshal config"))
			}
			if cProgtmp.Programs != nil {
				config.Programs = append(config.Programs, cProgtmp.Programs...)
			}
			if cProgtmp.Groups != nil {
				config.Groups = append(config.Groups, cProgtmp.Groups...)
			}
		}
		fmt.Println()
	}

	return &config
}
